﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using ZyGame.Drawing;
using ZyGame.Resource;
using ZyGame.Execute;
using UnityEngine;
using ZyGame;

namespace ZyGame.Avatar
{
    public enum PublishState : byte
    {
        None,
        Publish,
        Drafts,
        Process,
    }

    public sealed class ElementComponent : IDisposable
    {
        public GameObject gameObject;
        public ElementData elementData;

        private AvatarBuilded _builded;
        private Runnable runnable;

        public ElementComponent(AvatarBuilded builded)
        {
            _builded = builded;
        }

        public void Dispose()
        {
            if (gameObject is not null)
                GameObject.DestroyImmediate(gameObject);
            elementData = null;
            _builded = null;
        }

        public Runnable SetElementData(ElementData data)
        {
            if (data == null || string.IsNullOrEmpty(data.model) || string.IsNullOrEmpty(data.texture))
            {
                return Client.Running.FromException(new Exception(String.Format("bad element data:{0}", Newtonsoft.Json.JsonConvert.SerializeObject(data))));
            }

            Client.Running.TryGetOrCreate(data.element.ToString(), out runnable);
            runnable.Execute(CreateSettingElementModelRunnable, data);
            runnable.Execute(CreateSettingElementTextureRunnable, data);
            return runnable;
        }

        IEnumerator CreateSettingElementModelRunnable(Runnable runnable, ElementData element)
        {
            if ((elementData != null && elementData.model == element.model))
            {
                yield break;
            }
            string url = element.model.GetElementAssetBundlePath();
            Runnable<GameObject> modelLoadRunnable = Client.Resource.LoadNetworkAssetAsync<GameObject>(url, string.Empty, null, element.version, element.crc);
            modelLoadRunnable.Execute(OnLoadElementModelCompleted, element);
            yield return modelLoadRunnable.Waitting();
        }

        void OnLoadElementModelCompleted(Runnable<GameObject> executed, ElementData element)
        {
            if (executed == null || executed.EnsureExecutedSuccessful() == false)
            {
                string error = string.Format(ErrorInfo.NOT_FIND_THE_ELEMENT_ASSET, element.model) + "2";
                Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE);
                runnable?.SetResult(new Exception(error));
                return;
            }
            Client.Console.WriteLine("set element model", element.element);
            if (gameObject != null)
            {
                GameObject.DestroyImmediate(gameObject);
            }
            gameObject = executed.result;
            BonesData bones = BonesConfig.instance.GetConfig(element.element);
            if (bones == null)
            {
                this.gameObject.SetParent(_builded.gameObject, Vector3.zero, Vector3.zero, Vector3.one);
            }
            else
            {
                Transform parent = _builded.gameObject.transform.Find(bones.location_path);
                if (parent == null)
                {
                    runnable?.SetResult(new Exception(ErrorInfo.NOT_FIND_THE_ELEMENT_POINT));
                    Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, ErrorInfo.NOT_FIND_THE_ELEMENT_POINT);
                    GameObject.DestroyImmediate(this.gameObject);
                    return;
                }
                gameObject.SetParent(parent.gameObject, Vector3.zero, Vector3.zero, Vector3.one);
            }
            this.gameObject.ResetMaterialShader("Unlit/Transparent Cutout");
        }

        IEnumerator CreateSettingElementTextureRunnable(ElementData element)
        {
            if (gameObject == null || element == null)
            {
                yield break;
            }
            //todo 涂鸦
            if (element.publish_status == 2)
            {
                yield return CombineDrawingData(element);
                _builded.gameObject.ToCameraCenter();
                yield break;
            }
            Runnable<Texture2D> elementTextureLoadRunnable = Client.Resource.LoadAssetAsync<Texture2D>(element.texture, gameObject);
            elementTextureLoadRunnable.Execute(OnLoadElementTextureCompleted, element);
            yield return elementTextureLoadRunnable.Waitting();
        }

        IEnumerator CombineDrawingData(ElementData data)
        {
            Runnable<byte[]> download = Client.Web.DownloadData(data.texture);
            yield return download.Waitting();
            if (download == null || download.EnsureExecutedSuccessful() == false)
            {
                yield break;
            }
            List<DrawingData> layers = new List<DrawingData>();
            using (BinaryReader reader = new BinaryReader(new MemoryStream(download.result)))
            {
                Element element = (Element)reader.ReadByte();
                ElementData temp = Newtonsoft.Json.JsonConvert.DeserializeObject<ElementData>(reader.ReadString());
                Runnable<Texture2D> elementTextureLoadRunnable = Client.Resource.LoadAssetAsync<Texture2D>(temp.texture, gameObject);
                yield return elementTextureLoadRunnable.Waitting();
                if (elementTextureLoadRunnable == null || !elementTextureLoadRunnable.EnsureExecutedSuccessful())
                {
                    runnable?.SetResult(new Exception(string.Format(ErrorInfo.NOT_FIND_THE_ELEMENT_ASSET, temp.texture)));
                    Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, string.Format(ErrorInfo.NOT_FIND_THE_ELEMENT_ASSET, temp.texture) + 4);
                    yield break;
                }
                Texture2D texture = elementTextureLoadRunnable.result;
                byte layerCount = reader.ReadByte();
                for (int j = 0; j < layerCount; j++)
                {
                    layers.Add(DrawingData.GenerateToBinary(reader));
                }
                if (layers.Count <= 0)
                {
                    yield break;
                }
                SetElementTexture(layers.Combines(texture));
                this.elementData = data;
            }
        }

        void OnLoadElementTextureCompleted(Runnable<Texture2D> executed, ElementData element)
        {
            if (executed == null || !executed.EnsureExecutedSuccessful())
            {
                runnable?.SetResult(new Exception(string.Format(ErrorInfo.NOT_FIND_THE_ELEMENT_ASSET, element.texture) + 6));
                Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, string.Format(ErrorInfo.NOT_FIND_THE_ELEMENT_ASSET, element.texture) + 6);
                return;
            }
            elementData = element;
            SetElementTexture(executed.result);
        }

        public void SetElementTexture(Texture texture)
        {
            if (gameObject == null)
            {
                string error = string.Format(ErrorInfo.Please_set_the_model_first_before_setting_the_map, elementData?.element);
                runnable?.SetResult(new Exception(error));
                Client.Console.WriteError(error);
                return;
            }
            Renderer renderer = gameObject.GetComponentInChildren<Renderer>();
            if (renderer != null)
            {
                renderer.sharedMaterial.mainTexture = texture;
            }
        }

        public Texture2D GetElementTexture()
        {
            Renderer renderer = gameObject.GetComponentInChildren<Renderer>();
            if (renderer != null)
            {
                if (renderer.sharedMaterial.mainTexture is RenderTexture)
                {
                    return ((RenderTexture)renderer.sharedMaterial.mainTexture).ReadTexture2D();
                }
                return (Texture2D)renderer.sharedMaterial.mainTexture;
            }

            return default;
        }

        public void UploadElementData(string id, string name, byte[] bytes2, PublishState state, Action<ElementData> onCompleted)
        {
            Client.Tools.StartCoroutine(Executed());
            IEnumerator Executed()
            {
                Texture2D texture2D = Camera.main.Screenshot(256, 256, gameObject);
                byte[] iconDataBytes = texture2D.EncodeToPNG();
                RequestCreateFileData icon = new RequestCreateFileData(name + "_icon.png", iconDataBytes.GetMd5(), "image/png", "2", iconDataBytes.Length);
                UploadAssetResponse iconResponse = null;
                yield return AvatarExtension.UploadAsset(icon, iconDataBytes, (response, exception) =>
                {
                    if (exception is not null)
                    {
                        onCompleted(null);
                        return;
                    }

                    iconResponse = response;
                });
                if (iconResponse == null)
                {
                    onCompleted(null);
                    yield break;
                }

                RequestCreateFileData drawingData = new RequestCreateFileData(name + ".png", bytes2.GetMd5(), "image/png", "2", bytes2.Length);

                yield return AvatarExtension.UploadAsset(drawingData, bytes2, (response, exception) =>
                {
                    if (exception != null)
                    {
                        onCompleted(null);
                        return;
                    }

                    ElementData createElementData = new ElementData();
                    createElementData.name = name;
                    createElementData.id = id;
                    createElementData.texture = response.data.url;
                    createElementData.icon = iconResponse.data.url;
                    createElementData.model = elementData.model;
                    createElementData.element = elementData.element;
                    createElementData.model_name = elementData.model_name;
                    createElementData.publish_status = (byte)state;
                    onCompleted(createElementData);
                });
            }
        }
    }
}